package com.ycy.socket;

import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Collection;

@Slf4j
public abstract class BaseSocketIoServer {

    protected static final String TOKEN = "token";

    protected static final String CORP_CODE = "corpCode";

    protected static final String EVENT_SYSTEM = "_SYSTEM_";



    @Autowired
    private SocketIOServer socketIOServer;

    private final SocketIoSessionCache clientCache = new SocketIoSessionCache();

    /**
     * Spring IoC容器创建之后，在加载SocketIOServiceImpl Bean之后启动
     *
     * @throws Exception
     */
    @PostConstruct
    private void autoStartup() throws Exception {
        start();
    }

    /**
     * Spring IoC容器在销毁SocketIOServiceImpl Bean之前关闭,避免重启项目服务端口占用问题
     *
     * @throws Exception
     */
    @PreDestroy
    private void autoStop() throws Exception {
        stop();
    }

    /**
     * 获取连接时的监听器。如果希望做更多的事情，可以重写这个方法
     * @return
     */
    public ConnectListener getConnectListener() {
        return client -> {
            String key = this.getCustomClientKey(client);

            String token = client.getHandshakeData().getSingleUrlParam(TOKEN);

//            String corpCode = Optional.ofNullable(SecureUtil.getUser(token))
//                    .map(i -> i.getCorpId())
//                    .orElse(null);

            String corpCode = "haha";

            if (null == corpCode) {
                log.error("非有效用户，无法获取corpCode");
                client.sendEvent(EVENT_SYSTEM, "非有效用户，无法获取corpCode");
                client.disconnect();
                return;
            }

            // 保存进client的临时存储里，方便后续调用
            saveCorpCode(client, corpCode);

//            if (StringUtil.isBlank(key)) {
//                return;
//            }
            clientCache.putCache(key, corpCode, client);
        };
    }


    /**
     * 根据client，生成client的标识key。
     * 一般会使用<pre>client.getHandshakeData().getSingleUrlParam(MARKING_KEY);
     * @param client
     * @return
     */
    public abstract String getCustomClientKey(SocketIOClient client);

    /**
     * 获取断开连接时的监听器。如果希望做更多的事情，可以重写这个方法
     * @return
     */
    public DisconnectListener getDisconnectListener() {
        return client -> {
            String key = this.getCustomClientKey(client);
//            if (StringUtil.isBlank(key)) {
//                return;
//            }
            client.disconnect();
            clientCache.removeCache(key);
        };
    };

    /**
     * 添加事件监听器，由于添加事件监听器的方法比较复杂，所以使用了void方法。
     * @param socketIOServer
     */
    public abstract void addEventListener(SocketIOServer socketIOServer);

    public void start() throws Exception {
        // 监听客户端连接
        socketIOServer.addConnectListener(getConnectListener());

        // 监听客户端断开连接
        socketIOServer.addDisconnectListener(getDisconnectListener());

        // 处理自定义的事件，与连接监听类似
        addEventListener(socketIOServer);

        // 开启Websocket
        socketIOServer.start();
    }

    public void stop() {
        if (socketIOServer != null) {
            socketIOServer.stop();
            socketIOServer = null;
        }
    }

    public void sendMessage(String key, String eventName, Object data) {
//        if (StringUtils.isBlank(key)) {
//            return;
//        }
        SocketIOClient client = clientCache.get(key);
        if (client != null) {
            client.sendEvent(eventName, data);
        }
    }

    public void sendMessageByCorpCode(String corpCode, String eventName, Object data) {
        Collection<SocketIOClient> clients = clientCache.getAllByCorpCode(corpCode).values();

        for (SocketIOClient client : clients) {
            if (client != null) {
                client.sendEvent(eventName, data);
            }
        }
    }


    /**
     * 将corpCode保存进client的内存存储空间里
     * @param client
     * @param corpCode
     */
    public static void saveCorpCode(SocketIOClient client, String corpCode) {
        client.set(CORP_CODE, corpCode);
    }


    /**
     * 将corpCode从client的内存存储空间中获取出来
     * @param client
     * @return
     */
    public static String getCorpCode(SocketIOClient client) {
        return client.get(CORP_CODE);
    }

    public SocketIoSessionCache getClientCache() {
        return clientCache;
    }
}
